﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;
using SynoManager.AudioStation.Data;
using System.Threading;
using SynoManager.GUI;
using ThreadState=System.Diagnostics.ThreadState;
using SynoManager.Communication;

namespace SynoManager.AudioStation.Gui
{
    public partial class AudioStationTab : UserControl
    {
        private readonly AudioStationGuiPlugin plugin;
        private Playlist[] playlists;

        public AudioStationTab()
        {
            InitializeComponent();
            this.Text = "Audio Library";
        }

        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);
            RebuildTree();
        }

        public AudioStationTab(AudioStationGuiPlugin plugin)
            :this()
        {
            this.plugin = plugin;
        }

        private void refreshToolStripButton_Click(object sender, EventArgs e)
        {
            RebuildTree();
        }

        private void treeView_AfterSelect(object sender, TreeViewEventArgs e)
        {
            if (e.Node == libraryNode)
            {
                ShowLibrary();
            }
            else if (e.Node.Tag is Playlist)
            {
                ShowPlaylist((Playlist) e.Node.Tag);
            }
        }

        #region Rebuild Tree
        TreeNode playlistNode;
        TreeNode libraryNode;
        public void RebuildTree()
        {
            if (treeBackgroundWorker.IsBusy)
                return;

            treeView.Nodes.Clear();
            addToPlaylistToolStripMenuItem.DropDownItems.Clear();
            addToPlaylistToolStripDropDownButton.DropDownItems.Clear();

            libraryNode = treeView.Nodes.Add("__library__", "Library", "library", "library");
            playlistNode = treeView.Nodes.Add("__playlist__", "Playlists", "playlists", "playlists");

            playlistNode.Nodes.Add("Loading...");
            playlistNode.Expand();
            
            treeBackgroundWorker.RunWorkerAsync();
        }

        private void treeBackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            playlists = plugin.Service.GetPlaylists();
        }

        private void treeBackgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            playlistNode.Nodes.Clear();

            foreach (Playlist playlist in playlists)
            {
                playlistNode.Nodes.Add(playlist.Name, playlist.Name, "playlist", "playlist").Tag = playlist;
                addToPlaylistToolStripMenuItem.DropDownItems.Add(playlist.Name, null, addToPlaylist).Tag = playlist;
                addToPlaylistToolStripDropDownButton.DropDownItems.Add(playlist.Name, null, addToPlaylist).Tag = playlist;
            }

            addToPlaylistToolStripMenuItem.DropDownItems.Add(new ToolStripSeparator());
            addToPlaylistToolStripDropDownButton.DropDownItems.Add(new ToolStripSeparator());

            addToPlaylistToolStripMenuItem.DropDownItems.Add("New playlist", Properties.Resources.table_add,
                                                                   createPlaylistFromSongsToolStripButton_Click);
            addToPlaylistToolStripDropDownButton.DropDownItems.Add("New playlist", Properties.Resources.table_add,
                                                                   createPlaylistFromSongsToolStripButton_Click);

            playlistNode.Expand();

            addPlaylistButton.Enabled =
                deletPlaylistToolStripButton.Enabled =
                refreshToolStripButton.Enabled = true;
        }

        #endregion

        #region List

        private void ShowPlaylist(Playlist playlist)
        {
            selectedPlaylist = playlist;
            itemsPerRequest = 20;

            removeToolStripButton.Visible = removeToolStripMenuItem.Visible = true;

            ListItems(playlist.GetFiles);
        }

        private void ShowLibrary()
        {
            selectedPlaylist = null;
            itemsPerRequest = 100;

            removeToolStripButton.Visible = removeToolStripMenuItem.Visible = false;

            ListItems(plugin.Service.GetAll);
        }

        private void ShowSearchResults(MusicFindField field, string keyword)
        {
            selectedPlaylist = null;
            itemsPerRequest = 100;

            removeToolStripButton.Visible = removeToolStripMenuItem.Visible = false;
            
            MusicSorting sorting = MusicSorting.Artist;
            SortDirection direction = SortDirection.Ascending;

            ListItems(delegate(int offset, int count, out int total)
                          {
                              return plugin.Service.Find(field, keyword, sorting, direction, offset, count, out total);
                          });
        }

        private Playlist selectedPlaylist;

        private delegate MusicFile[] ListItemsDelegate(int offset, int count, out int total);
        private ListItemsDelegate queuedDelegate;
        private void ListItems(ListItemsDelegate del)
        {
            if (listBackgroundWorker.IsBusy)
            {
                queuedDelegate = del;
                listBackgroundWorker.CancelAsync();
                return;
            }
            queuedDelegate = null;

            stopLoadButton.Visible = true;
            loadProgressBar.Visible = true;

            loadProgressBar.Style = ProgressBarStyle.Marquee;

            listView.Items.Clear();
            listBackgroundWorker.RunWorkerAsync(del);
        }

        private int itemsPerRequest = 10;
        private void listBackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            ListItemsDelegate listItemsDelegate = (ListItemsDelegate) e.Argument;

            int totalItems = 0;
            int offset = 0;

            while (offset == 0 || offset < totalItems)
            {
                MusicFile[] files = listItemsDelegate(offset, itemsPerRequest, out totalItems);
                Invoke(new AddFilesDelegate(AddFiles), (object)files);

                if (listBackgroundWorker.CancellationPending)
                    return;

                decimal progress = (totalItems == 0) ? 0 : ((decimal) 100/(decimal) totalItems)*offset;
                Debug.Assert(progress >= 0 && progress <= 100);
                listBackgroundWorker.ReportProgress((int)progress, String.Format("Loading playlist items {0} of {1}", offset, totalItems));
                offset += itemsPerRequest;
            }
        }

        private delegate void AddFilesDelegate(MusicFile[] files);
        private void AddFiles(MusicFile[] files)
        {
            foreach(MusicFile file in files)
            {
                ListViewItem lvi = listView.Items.Add(file.Title);
                lvi.ImageKey = file.IsInvalid ? "unknownsong" : "song";
                lvi.SubItems.Add(file.Artist);
                lvi.SubItems.Add(file.Album);
                lvi.SubItems.Add(file.Genre);
                lvi.SubItems.Add(file.Duration.TotalMinutes.ToString("00") + ":" + file.Duration.Seconds.ToString("00"));
                lvi.SubItems.Add(file.Path);
                lvi.SubItems.Add(file.ID.ToString());
                lvi.Tag = file;
            }
        }

        private void listBackgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            loadProgressBar.Style = ProgressBarStyle.Blocks;
            loadProgressBar.Value = e.ProgressPercentage;
            loadProgressBar.ToolTipText = e.UserState.ToString();
        }

        private void listBackgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            stopLoadButton.Visible = false;
            loadProgressBar.Visible = false;

            if (queuedDelegate != null)
                ListItems(queuedDelegate);
        }

        private void stopLoadButton_Click(object sender, EventArgs e)
        {
            listBackgroundWorker.CancelAsync();
        }

        #endregion

        #region Tree Context Menu
        private void treeContextMenu_Opening(object sender, CancelEventArgs e)
        {
            e.Cancel = !(treeView.SelectedNode.Tag is Playlist);
        }

        private void setAsActivePlaylistToolStripMenuItem_Click(object sender, EventArgs e)
        {

        }

        private void deletePlaylistToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Playlist playlist = (Playlist) treeView.SelectedNode.Tag;
            if (MessageBox.Show("Are you sure you want to delete this playlist", "Delete Audio Station Playlist", MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button1) == DialogResult.Yes)
            {
                playlist.Delete();
                treeView.SelectedNode.Remove();
            }
        }

        private void renamePlaylistToolStripMenuItem_Click(object sender, EventArgs e)
        {
            treeView.SelectedNode.BeginEdit();
        }

        private void treeView_BeforeLabelEdit(object sender, NodeLabelEditEventArgs e)
        {
            if (treeView.SelectedNode.Tag is Playlist)
                return;
            if (treeView.SelectedNode.Tag is MusicFile[])
                return;
            if (treeView.SelectedNode.Tag is int[])
                return;
            e.CancelEdit = true;
        }

        private void treeView_AfterLabelEdit(object sender, NodeLabelEditEventArgs e)
        {
            if (e.Node.Tag is Playlist)
            {
                Playlist playlist = (Playlist)e.Node.Tag;
                try
                {
                    playlist.Name = e.Label;
                }
                catch (ResultErrorException ex)
                {
                    //
                    MessageBox.Show("Could not rename playlist:\n" + GetErrorMessage(ex.Key), "AudioStation", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    e.CancelEdit = true;
                }
            }
            else if (e.Node.Tag is MusicFile[])
            {
                if (e.Label == null)
                {
                    e.Node.Remove();
                }
                else
                {
                    MusicFile[] files = (MusicFile[]) e.Node.Tag;
                    try
                    {
                        Playlist playlist = plugin.Service.CreatePlaylist(e.Label, files);
                        e.Node.Tag = playlist;
                        treeView.SelectedNode = e.Node;
                    }
                    catch (ResultErrorException ex)
                    {
                        MessageBox.Show("Could not create playlist:\n" + GetErrorMessage(ex.Key), "AudioStation", MessageBoxButtons.OK, MessageBoxIcon.Error);
                        e.Node.Remove();
                    }
                }
            }
        }

        public string GetErrorMessage(string errorCode)
        {
            switch (errorCode)
            {
                case "playlist_name_exist":
                    return "The name of the playlist already exists";
                default:
                    return "Unknown error (" + errorCode + ")";
            }
        }
        #endregion

        #region ListView Toolstrip
        private void listView_SelectedIndexChanged(object sender, EventArgs e)
        {
            removeToolStripButton.Enabled =
                removeToolStripMenuItem.Enabled =
                addToPlaylistToolStripDropDownButton.Enabled =
                addToPlaylistToolStripDropDownButton.Enabled =
                    listView.SelectedItems.Count > 0;
        }

        private void removeToolStripButton_Click(object sender, EventArgs e)
        {
            // this asserts that the file.index always equals the playlist index

            List<int> ids = new List<int>();
            foreach(ListViewItem lvi in listView.SelectedItems)
            {
                //MusicFile file = (MusicFile) lvi.Tag;
                ids.Add(lvi.Index);
            }
            selectedPlaylist.RemoveSongs(ids.ToArray());

            while (listView.SelectedItems.Count > 0)
                listView.SelectedItems[0].Remove();
        }

        private void addToPlaylist(object sender, EventArgs e)
        {
            ToolStripItem item = (ToolStripItem) sender;
            Playlist playlist = (Playlist) item.Tag;

            List<int> ids = new List<int>();
            foreach(ListViewItem lvi in listView.SelectedItems)
            {
                MusicFile file = (MusicFile) lvi.Tag;
                ids.Add(file.ID);
            }

            playlist.AddSongs(ids.ToArray());
        }

        #endregion

        #region Create Playlist
        private void createEmptyPlaylist(object sender, EventArgs e)
        {
            CreatePlaylist(new MusicFile[0]);
        }

        private void createPlaylistFromSongsToolStripButton_Click(object sender, EventArgs e)
        {
            List<MusicFile> files = new List<MusicFile>();
            foreach (ListViewItem lvi in listView.SelectedItems)
            {
                MusicFile file = (MusicFile)lvi.Tag;
                files.Add(file);
            }

            CreatePlaylist(files.ToArray());
        }

        private void CreatePlaylist(MusicFile[] files)
        {
            TreeNode newNode = new TreeNode("New Playlist");
            newNode.ImageKey = newNode.SelectedImageKey = "playlist";
            newNode.Tag = files;
            playlistNode.Nodes.Add(newNode);
            Thread.Sleep(0);
            treeView.SelectedNode = newNode;
            treeView.LabelEdit = true;
            Debug.Assert(!newNode.IsEditing);
            newNode.BeginEdit();
            //newNode.BeginEdit();
        }

        #endregion

        #region Search
        private void searchAllCategoriesToolStripMenuItem_Click(object sender, EventArgs e)
        {
            ShowSearchResults(MusicFindField.All, searchQueryTextBox.Text);
        }

        private void searchArtistsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            ShowSearchResults(MusicFindField.Artist, searchQueryTextBox.Text);
        }

        private void searchTitlesToolStripMenuItem_Click(object sender, EventArgs e)
        {
            ShowSearchResults(MusicFindField.Title, searchQueryTextBox.Text);
        }

        private void searchAlbumsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            ShowSearchResults(MusicFindField.Album, searchQueryTextBox.Text);
        }

        private void searchGenresToolStripMenuItem_Click(object sender, EventArgs e)
        {
            ShowSearchResults(MusicFindField.Genre, searchQueryTextBox.Text);
        }

        #endregion
    }
}
